到目前為止,已經說明了 docker run
幾個常用的選項和參數,也做了一些簡單的範例。今天將以情境的方式,介紹如何應用 docker run
指令完成任務。
使用 container 連線資料庫,如:
docker run --rm -it percona mysql -h10.10.10.10 -umiles -pchou
筆者曾遇過在連線資料庫的時候,遇到版本不相容的問題,所以有做過下面的測試:
# 換用其他 fork 的 client
docker run --rm -it mysql mysql -h10.10.10.10 -umiles -pchou
docker run --rm -it mariadb mysql -h10.10.10.10 -umiles -pchou
# 換用其他版本
docker run --rm -it percona:5.6 mysql -h10.10.10.10 -umiles -pchou
docker run --rm -it percona:5.7 mysql -h10.10.10.10 -umiles -pchou
最終成功地完成測試了。如果要在本機安裝多個版本會是困難的,相較使用具隔離性的 Docker container 測試起來就非常容易。
Redis 也可以用一樣的方法:
docker run --rm -it redis redis-cli -h10.10.10.10
docker run --rm -it redis:4 redis-cli -h10.10.10.10
docker run --rm -it redis:3 redis-cli -h10.10.10.10
開發階段在測試的時候,最怕遇到多人共用資料庫的情境,因為會互相傷害影響測試結果。最好的方法還是人人自備資料庫,自己的資料自己建。
但像筆者對機器管理不熟悉,就算照著網路教學自己建好資料庫,但遇到問題或開不起來,也只會重灌治萬病,自己建資料庫的做法是有風險的。
Docker 在這種情境下,會是個非常好用的工具。container 砍掉重練的效果就等於重灌,而且常見的 image 幾乎都找得到,一個 docker pull
指令就搞定了,連學習安裝過程和踩雷的時間都可以省下來。配合 -p
選項把 port 開放出去,那就跟在本機安裝 server 幾乎完全一樣,非常方便。
docker run --rm -it -p 3306:3306 -e MYSQL_ROOT_PASSWORD=pass mysql
docker run --rm -it -p 5432:5432 -e POSTGRES_PASSWORD=pass postgres
docker run --rm -it -p 6379:6379 redis
docker run --rm -it -p 11211:11211 memcached
筆者主要語言為 PHP,建置環境時,偶爾會需要用到 node.js。因為極少用到 node.js,所以不想額外安裝 nvm 等相關套件,但要用的當下又很麻煩,這有什麼方法能解決呢?
Docker 提供了滿滿的大平台,只要透過下面這個 docker run
指令,即可達成「不安裝工具還要能使用工具」的任性需求:
docker run --rm -it -v $PWD:/source -w /source node:14-alpine npm -v
來分解並複習一下這些選項與參數的用途:
--rm
執行完後移除。當要使用功能的時候開 container,功能處理完後移除 container-it
通常需要跟 container 互動,因此會加這個選項-v $PWD:/source
把本機目錄綁定到 container,$PWD
為執行指令時的當下目錄,/source
則是 container 裡的絕對路徑-w /source
是進去 container 時,預設會在哪個路徑下執行指令node:14-alpine
image 名稱,這裡用了 Alpine 輕薄短小版npm -v
在 container 執行的指令,可以視需求換成其他指令步驟有點複雜,有個方法是將它拆解成「進 container」與「container 裡執行指令」兩個步驟執行。在不清楚 container 發生什麼事的時候,拆解指令是個非常好用的方法;相反地,了解 container 運作過程的話,合併指令則是方便又直接,讀者可視情況運用。
# 先查看目前檔案列表
ls -l
# 使用 shell
docker run --rm -it -v $PWD:/source -w /source node:14-alpine sh
# 進入 container 執行指令
npm -v
# 進入做其他事看看
npm init
# 離開 container 看看,多了一個 packages.json 檔案
ls -l
上面這個範例可以看到,因為有做 bind mount,所以指令在 container 做的事情會同步回 host。簡單來說即:Host 沒有安裝指令也不打緊,用 Docker 啟動 container 幫忙執行後,再把結果傳回給 host。
接著拿上面產生的 packages.json
來做一個正式的範例:
# 確認內容
cat packages.json
# 安裝套件
docker run --rm -it -v $PWD:/source -w /source node:14-alpine npm install
# 確認產生的檔案
ls -l
# 再執行一次觀察差異
docker run --rm -it -v $PWD:/source -w /source node:14-alpine npm install
這個範例主要可以觀察到兩件事:
docker run
時,因為沒有 packages-lock.json
,所以 npm 有產生這個檔案,ls -l
也有看到docker run
時,已經有 packages-lock.json
了,所以 npm 做的事跟第一次不一樣上面這個 docker run
指令即可安裝 packages.json
所需套件。
每次打落落長的指令也很逼人,有個簡單的解決方案--設定 alias。
# 確認無法使用 npm
npm -v
# 設定 alias
alias npm="docker run -it --rm -v \$PWD:/source -w /source node:14-alpine npm"
# 確認可以透過 docker run 使用 npm
npm -v
設定好後,打 npm
就等於打了長長一串 docker run
指令了。到這裡讀者也能感受,最後用起來的感覺會跟平常使用 npm
一模一樣,幾乎可以取代安裝工具。
類似的概念可以做到非常多工具上,下面是筆者目前實驗過可行的。
# 使用 Composer
alias composer="docker run -it --rm -v \$PWD:/source -w /source composer:1.10"
# 使用 npm
alias npm="docker run -it --rm -v \$PWD:/source -w /source node:14-alpine npm"
# 使用 Gradle
alias gradle="docker run -it --rm -v \$PWD:/source -w /source gradle:6.6 gradle"
# 使用 Maven
alias mvn="docker run -it --rm -v \$PWD:/source -w /source maven:3.6-alpine mvn"
# 使用 pip
alias pip="docker run -it --rm -v \$PWD:/source -w /source python:3.8-alpine pip"
# 使用 Go
alias go="docker run -it --rm -v \$PWD:/source -w /source golang:1.15-alpine go"
# 使用 Mix
alias mix="docker run -it --rm -v \$PWD:/source -w /source elixir:1.10-alpine mix"
最後筆者要提醒一個重點:這個範例主要是想讓讀者知道 Docker 可以如何應用。實務上,跟安裝好工具的使用經驗還是有所差別的。
因工作目錄是設定 -w /source
,也許會因專案設定不同而導致工具執行出錯;另外,工具如果跟某個絕對路徑有相依,如 ~/.npm
,這也可能導致出錯。
Container Kernel 不同可能會導致無法預期的錯誤。
以 Mac + Docker Desktop for Mac 來說,實際執行 container 是使用 Linux kernel,因此若工具有產生 binary,通常會是 for Linux,這時回到 Mac 直接使用就會出錯。
另外,Container Kernel 不同,在做 bind mount 時會有效能問題,簡單來說就是會跑比較慢一點。
若讀者有個好習慣是時常用 --rm
的話,那大部分的情況可以把這個標題大聲說出來:「Docker 上跑就沒問題」。
舉個例,laravel-bridge/scratch
套件需要在 PHP >= 7.1 版的環境裡執行單元測試,可以使用下面指令來測試套件在各個環境是否正常:
# 在 laravel-bridge/scratch 上跑測試
docker run -it --rm -v $PWD:/source -w /source php:7.1-alpine vendor/bin/phpunit
docker run -it --rm -v $PWD:/source -w /source php:7.2-alpine vendor/bin/phpunit
docker run -it --rm -v $PWD:/source -w /source php:7.3-alpine vendor/bin/phpunit
docker run -it --rm -v $PWD:/source -w /source php:7.4-alpine vendor/bin/phpunit
全部 pass,這樣就比較有信心跟其他開發者說,在 PHP 7.1 ~ 7.4 上都是沒問題的!
同樣地,我們也可以在 PHP 8.0 beta 上測試,來確保套件在即將發布的最新版上也是能正常運作的:
docker run -it --rm -v $PWD:/source -w /source php:8.0.0beta4-alpine vendor/bin/phpunit
docker run
-w|--workdir
指定預設執行的路徑今天比較像是筆者如何使用 Docker 的小技巧,有些情境都是不想裝或筆者不會裝,所以才會直接拿別人包好的 image 來用;有些情境則是確認使用官方 image 執行可以正常,則不管在哪個用一樣的方法執行也要正常。
請問我在windows 裏安裝docker desktop,在windows terminal 裏輸入
docker run -it --rm -v d:\docker\source:/source -w /source php:7.1-alpine vendor/pedroroccon/my-package
它顯示以下這個錯誤,是否我輸入錯了,還是要在linux 輸入就正常?
/usr/local/bin/docker-php-entrypoint: exec: line 9: vendor/pedroroccon/my-package: not found
Thank you
感覺是資料夾沒有綁定進去,Linux 應該是可行的
我在ubuntu 執行了"docker run -it --rm -v $PWD:/source -w /source php:7.4-alpine vendor/bin/phpunit"
是顯示錯誤。